
package Controller;

import Controller.Exceptions.ControllerPerformException;
import Controller.Exceptions.ControllerReceivedException;
import Controller.Interface.ReceivedInterface;
import Controller.Interface.ListObservable;
import Controller.Interface.PerformInterface;
import GUI.ChatGUI;
import GUI.Interface.ListObserveur;
import Network.ChatNetwork;
import Network.Exceptions.NetworkException;
import Model.Exceptions.FileIdException;
import Model.FileId;
import Model.PathFile;
import Model.Status;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 * La classe Chatcontroller prend en charge la gestion des événements de 
 * synchronisation pour mettre à jour la vue ou le modèle et les synchroniser.
 * Il reçoit tous les événements de l'utilisateur et enclenche les actions à 
 * effectuer.Le ChatContrôleur n'effectue aucun traitement et implemente deux interfaces:
 * <ul>
 * <li>PerformInterface pour la gestion des evenements venant de la GUI
 * <li>ReceivedInterface pour la gestion des évenements venant du ChatNetwork
 * </ul>
 * <p>Les methodes du ChatController chargées de la gestion des événements venant 
 * de la GUI lèvent une exception de type  <tt>ControllerPerformException</tt>
 * 
 * <p>Les methodes du ChatController chargées de la gestion des événements venant 
 * du ChatNetwork lèvent une exception de type  <tt>ControllerReceivedException</tt>
 * 
 * @author      Frank Djomo
 * @author      Christelle SIAGO HAPPI
 * @author      djomopoueme@gmail.com
 * @author      christelle.siagohappi@gmail.com
 * @version     1.0
 * @see     ChatGUI
 * @see     ChatNetwork
 */
public class ChatController implements PerformInterface,ReceivedInterface,ListObservable{

    private   ChatNetwork    ni;
    private   ChatGUI        gui;

    private   ArrayList<ListObserveur> listObserveur;
    /**
    * Constructeur
    */
    public  ChatController(){
        listObserveur = new ArrayList<ListObserveur>();
    }   
    /**
    * Affecte un ChatGUI à notre contrôleur
    * @param gui ChatGUI à affecter
    */
    public void setGUI(ChatGUI gui){
        this.gui =gui;
        this.addObservers(gui);
    }   
    /**
    * Affecte un ChatNI à notre contrôleur
    * @param ni ChatNI à affecter
    */
    public void setNI(ChatNetwork ni){
        this.ni = ni;
    }
    /**
    * Gère la connexion d'un utilisateur local
    * @param login Login fourni par l'utilisateur
    * @throws ControllerPerformException
    */
    @Override
    public void performConnect(String login) throws ControllerPerformException {
         if(!login.equals("")){       
            try {
                ni.sendHello(login, true);
            } catch (NetworkException ex) {
                Logger.getLogger(ChatController.class.getName()).log(Level.SEVERE, null, ex);
                throw new ControllerPerformException("Erreur lors du performConnect", ex);
            }
         }
    }
    /**
    * Gère la déconnexion de l'utilisateur local
    * @throws ControllerPerformException
    */
    @Override
    public void performDisonnect() throws ControllerPerformException {
        try {
            ni.sendBye(); 
        } catch (NetworkException ex) {
            Logger.getLogger(ChatController.class.getName()).log(Level.SEVERE, null, ex);
           throw new ControllerPerformException("Erreur l'envoi d'un Bye",ex);
        } 
    }
     /**
    * Gère l'envoi d'un message texte
    * @param remoteLogin Login de l'utilisateur distant à qui envoyer le message
    * @param messageText Message à envoyer
    * @throws ControllerPerformException
    */
    @Override
    public void performChat(String remoteLogin, String messageText) throws ControllerPerformException { 
        try {
            //Envoi du message
            ni.sendChatMessage(remoteLogin, messageText);
        } catch (NetworkException ex) {
            Logger.getLogger(ChatController.class.getName()).log(Level.SEVERE, null, ex);
            throw new ControllerPerformException("Echec envoi message",ex.getCause());
        }  
    }
    /**
    * Gère l'envoi d'une demande de transfert de fichier
    * @param file Pathfile representant le fichier à transferer
    * @param remoteLogin Login de l'utilisateur distant à qui envoyer le message
    * @throws ControllerPerformException
    */
    @Override
    public void performFileRequest(PathFile file, String remoteLogin) throws ControllerPerformException {
        try {
            //Recuperation de l'addresse distante
            //Nouveau FileID pour le fichier
            FileId idFileToSend=new FileId();
            //taille d'envoi du fichier controlé en amont
            ni.sendFileRequest(remoteLogin, file, idFileToSend, file.getFile().length());
        } catch (NetworkException ex) {
            Logger.getLogger(ChatController.class.getName()).log(Level.SEVERE, null, ex);
            throw new ControllerPerformException("Echec Envoi FileRequest",ex);
        } catch (FileIdException ex) {
            Logger.getLogger(ChatController.class.getName()).log(Level.SEVERE, null, ex);
            throw new ControllerPerformException("Echec creation FileID",ex);
        }
     
    }
     /**
    * Gère l'envoi d'une reponse de transfert de fichier
    * @param remoteLogin
    * @param accept <code>true</code> si demande accepté 
    *               <code>false</code> sinon
    * @throws ControllerPerformException
    */
    @Override
    public void performFileResponse(String remoteLogin, boolean accept) throws ControllerPerformException {
        try {        
         
             ni.sendFileResponse(remoteLogin,accept);
             //TODO passer infos pour ServerTCP
        } catch (NetworkException ex) {
            Logger.getLogger(ChatController.class.getName()).log(Level.SEVERE, null, ex);
            throw new ControllerPerformException("Echec Envoi FileResponse",ex);
        } 
    }
    /**
    * Gère l'envoi d'un wizz à un utilisateur distant
    * @param remoteLogin Login de l'utilisateur distant à qui envoyer le wizz
    * @throws ControllerPerformException
    */
    @Override
    public void performWizzer(String remoteLogin) throws ControllerPerformException {
        try {
            ni.sendWizz(remoteLogin);
        } catch (NetworkException ex) {
            Logger.getLogger(ChatController.class.getName()).log(Level.SEVERE, null, ex);
            throw new ControllerPerformException("Echec Envoi Wizz",ex);
        }
    }
     /**
    * Gère l'envoi d'un changement de status
    * @param show  Status de l'utilisateur
    * @throws ControllerPerformException   
    */
    @Override
    public void performStatus(String show) throws ControllerPerformException {
        Status s=Status.AWAY;;
        if(show.equals((Status.CHAT).toString())){
            s=Status.CHAT;
        }
        try {
            ni.sendPresence(s);
        } catch (NetworkException ex) {
            Logger.getLogger(ChatController.class.getName()).log(Level.SEVERE, null, ex);
           throw new ControllerPerformException("Echec Envoi Status",ex);

        }
    }
    /**
    * Gère l'envoi d'une notification d'ecriture 
    * @param remoteLogin  Login de l'utilisateur distant à qui envoyer la notification
    * @param isWriting   <code>true</code> si utilisateur ecrit 
    *                    <code>false</code> sinon
    * @throws ControllerPerformException
    */
    @Override
    public void performComposing(String remoteLogin,boolean isWriting) throws ControllerPerformException {
        try {
            ni.sendComposing(remoteLogin,isWriting);
        } catch (NetworkException ex) {
            Logger.getLogger(ChatController.class.getName()).log(Level.SEVERE, null, ex);
            throw new ControllerPerformException("Echec Envoi Composing",ex);
        }
    }
    /**
    * Gère la reception d'un Hello distant 
    * @param remoteLogin Login de l'utilisateur distant
    * @param request     Type de requete Hello
    *                    <code>true</code> si Hello Request 
    *                    <code>false</code> si Hello Reply
    * @param address    Adresse IP de l'utilisateur distant
    * @throws ControllerReceivedException
    */   
    @Override
    public void receivedHello(String remoteLogin, boolean request, InetAddress address) throws ControllerReceivedException {
        //Si l'utilisateur n'est pas dans la liste
        notifyObserversToAdd(remoteLogin);
        System.out.println("connexion du remoteUser "+remoteLogin+"@"+address);
        //Si c'est un hello request.
        if(request==true && gui.isConnected()){
            try {
                ni.sendHelloUnicast(address,false);
            } catch (NetworkException ex) {
                Logger.getLogger(ChatController.class.getName()).log(Level.SEVERE, null, ex);
                throw new ControllerReceivedException("Erreur lors du tranfert de fichier",ex);
            }
        }
    }
    /**
    * Gère la reception d'un Bye distant 
    * @param remoteLogin Login de l'utilisateur distant
    */
    @Override
    public void receivedBye(String remoteLogin) {
        notifyObserversToRemove(remoteLogin);       
    }
    /**
    * Gère la reception d'un message texte
    * @param message Message texte recu
    * @param remoteLogin Login de l'utilisateur distant
    */
    @Override
    public void receivedChat(String message, String remoteLogin) {
        gui.refreshMsg(message,remoteLogin);
    }
    /**
    * Gère la reception d'une demande de transfert de fichier
    * @param pathfile       PathFile du fichier à transferer
    * @param remoteLogin    Login de l'utilisteur distant
    * @see PathFile
    */
    @Override
    public void receivedFileRequest(PathFile pathfile, String remoteLogin){
        gui.refreshFileRequest(pathfile,remoteLogin);
    }
    /**
    * Gère la reception d'une reponse de transfert de fichier
    * @param accept      <code>true</code> si demande accepté 
    *                    <code>false</code> sinon
    */
    @Override
    public void receivedFileResponse(boolean accept){
        //reponse à une requete
         gui.refreshFileResponse(accept);               
            
    }
    /**
    * Gère la reception d'un wizz à un utilisateur distant
    * @param remoteLogin Login de l'utilisateur distant à qui envoyer le wizz
    */
    @Override
    public void receivedWizzer(String remoteLogin) {
            gui.refreshWizz(remoteLogin);
            System.out.println("Reception d'un wizz de "+remoteLogin);

    }
    /**
    * Gère la reception d'une notification de status
    * @param remoteLogin Login de l'utilisateur distant
    * @param status  Status de l'utilisateur distant
    * @throws ControllerReceivedException
    */
    @Override
    public void receivedStatus(String remoteLogin, String status) {
        gui.refreshPresence(remoteLogin,status);
        System.out.println("notification de status "+remoteLogin+" status: "+status);

    }
    /**
    * Gère la reception d'une notification d'ecriture 
    * @param remoteLogin Login de l'utilisateur distant
    * @param isWriting   <code>true</code> si utilisateur ecrit 
    *                  <code>false</code> sinon 
    * @throws ControllerReceivedException
    */
    @Override
     public void receivedComposing(String remoteLogin, boolean isWriting ){
        gui.refreshComposing(remoteLogin,isWriting);
        System.out.println("Notification d'ecriture "+remoteLogin);
    }
    /**
    * Ajoute un observateur à la liste
    * @param obs  Observateur qui implemente l'interface ListObservable
    */
    @Override
    public void addObservers(ListObserveur obs) { 
        listObserveur.add(obs); 
    } 
    /**
    * Supprime un observateur à la liste
    */
    @Override
    public void delObservers() { 
        listObserveur.clear(); 
    }  
    /**
    * Avertit l'observateur d'un ajout
    * @param name  nom de l'utilisateur à rajouter
    */
    @Override
    public void notifyObserversToAdd(String name)
    {
        for(ListObserveur obs : listObserveur ) 
            obs.addElement(name);
        
    } 
    /**
    * Avertit l'observateur d'un retrait
    * @param name  nom de l'utilisateur à enlever
    */
    @Override
    public void notifyObserversToRemove(String name)
    {
        for(ListObserveur obs : listObserveur ) 
            obs.removeElement(name);
        
    }
}
